import java.sql.Time;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Random;


public class Population {

	static int TOURNAMENT_SIZE = 2;
	static float WIN_CHANCE = 0.9f;
	
	boolean pointVersion;
	int genNum = 0;
	int populationSize = 0;
	float pCrossover = 0.8f;
	float pMutation = 0.05f;
	float pReproduction = 1-pCrossover - pMutation;
	Individual_Factory primordialSoup;
	
	ArrayList<Individual> oldGen = null;
	ArrayList<Individual> newGen = null;
	ArrayList<Individual> tournamentPool = null;
	
	Random rand = null;
	
	public Population(int popSize, int treeSize, boolean pointy, Random r){
		
		populationSize = popSize;
		pointVersion = pointy;
		rand = r;
		
		System.out.println("Point? " + pointVersion);
		
		oldGen = new ArrayList<Individual>();
		newGen = new ArrayList<Individual>();
		tournamentPool = new ArrayList<Individual>(TOURNAMENT_SIZE);
		primordialSoup = new Individual_Factory(treeSize,rand);
		
		for(int y=0; y<popSize; y++){
			oldGen.add(primordialSoup.getIndividual());
		}
		
		tournamentPool.add(oldGen.get(0));
		tournamentPool.add(oldGen.get(1));
	}
	
	public Object[] doGeneration(){
		Object[] arrRet = new Object[5];
		updateFitness();
		float operation = 0;
		double returner = Double.MAX_VALUE;
		Individual fittest = null;
		double averageFitness = 0; 
		double averageSize = 0;
		int bestSize = 0;
		int oldGenSize = oldGen.size();
		
		for(Individual each:oldGen){
			averageFitness = averageFitness + each.fitness;
//			System.out.println(averageFitness);
			int rootSize = each.root.getSize();
			averageSize = averageSize + rootSize;
			if(each.fitness<returner){
				fittest = each;
				bestSize = rootSize;
				returner = each.fitness;
				arrRet[1] = each.toString();
			}	
		}
		
//		System.out.println(averageFitness);
		averageFitness = averageFitness / (double)oldGenSize;
		averageSize = averageSize / (double)oldGenSize;
		
		newGen.add(fittest.deepCopy());
		
		while(newGen.size() < populationSize){
//			System.out.println("New generation size: " + newGen.size());
			//choose operation
			operation = rand.nextFloat();
			if(operation < pCrossover){
//				System.out.println("Stuck at crossover");
				Individual unlucky = offerSacrifice(rand);
//				System.out.println("Stuck at offer sacrifice 1");
				Individual unfortunate = offerSacrifice(rand);
//				System.out.println("Stuck at offer sacrifice 2");
				if(pointVersion){
					unlucky.pointCrossOver(unfortunate, rand);
				}else{
					unlucky.crossOver(unfortunate, rand);
				}
				newGen.add(unlucky);
				newGen.add(unfortunate);
			}else if(operation < pCrossover+pMutation){
//				System.out.println("Stuck at mutation");
				Individual scion = offerSacrifice(rand);
				if(pointVersion){
					scion.pointMutation(primordialSoup, rand);
				}else{
					Individual shortLived = primordialSoup.getIndividual();
					scion.crossOver(shortLived, rand);
				}
				newGen.add(scion);
			}else{//reproduction
//				System.out.println("Stuck at reproduction");
				newGen.add(offerSacrifice(rand));
			}
			
		}//end while
		oldGen = newGen;
		newGen = new ArrayList<Individual>();
		
		arrRet[0] = returner;
		arrRet[2] = bestSize;
		arrRet[3] = averageFitness;
		arrRet[4] = averageSize;
		
		return arrRet;
	}
	
	private void updateFitness(){
		for(Individual each:oldGen){
			each.fitness = Fitness.getFitness(each);
		}
	}
	
	private Individual offerSacrifice(Random r){
		int victim = 0;
		for(int k=0; k<TOURNAMENT_SIZE; k++){
			victim = r.nextInt(oldGen.size());
			tournamentPool.set(k,oldGen.get(victim));
		}
		Collections.sort(tournamentPool);
		for(int a=0; a<TOURNAMENT_SIZE; a++){
			if(r.nextDouble()<WIN_CHANCE*Math.pow((1-WIN_CHANCE),a)){
				return tournamentPool.get(a).deepCopy();
			}
		}
		return tournamentPool.get(0).deepCopy();
	}
}
